跳到主要内容

09 ADC 电压采集实验

在这里插入图片描述

开发板官网https://www.edevkit.com/)

本系列教程目录:


ADC 电压采集实验

本章目标:

  • 了解 ADC 基本概念和 RA6M5 处理器的 ADC 模块;
  • 学会使用 RASC 配置 ADC 采样引脚的模拟信号;

硬件设计

本实验使用开发板的 ADC 模块,通过 ADC 通道采样外部电压信号,并由串口输出采集数据。

软件设计

新建工程

复制一份“07 SCI-UART_Recv_send”修改文件夹名“09 ADC”,双击打开 EBF_RA6M5.uvprojx 工程,通过 Keil 的新建文件新建 adc.cadc.h,保存到目录下的 \src 文件夹下。

FSP 配置

Keil 界面选择“Tools”→“RA Smart Configurator”打开 RASC 界面。

首先配置 ADC 通道,依次点击“Stacks”→“Pins”→“Peripherals”→“ADC0”来配置通道 AN000 对应的引脚为 P000。

插图01

下一步点击“Stacks”→“New Stack”→“Analog”→“ADC (r_adc)”来添加 ADC 模块。

插图02

下一步配置 ADC 通道 0 的属性。

插图03

在“Input”→“Channel Scan Mask”选择通道 0。

插图04

ADC属性描述
General > Name模块实例名
General > Unit指定要使用的 ADC 单元
General > Resolution指定转换分辨率
General > Alignment指定转换结果对齐方式
General > Clear after read读取转换结果后自动清除
General > Mode模式
Single Scan:单次扫描
Continuous Scan:连续扫描
Group Scan:组扫描
General > Double-trigger双触发
Input > Channel Scan Mask
(channel availability varies by MCU)
该通道位掩码用于使能 ADC 通道;
若是在组模式下,则是用于指定哪些通道归属于扫描组 A。
这里要勾选 “Channel 0”,图上由于篇幅关系没有标出来
Interrupts > Normal/Group A Trigger正常模式下的或组模式下组 A 的触发类型。
这里按照默认,选择软件触发
Interrupts > Callback中断回调函数。设置为 adc_callback
Interrupts > Scan End Interrupt Priority扫描完成中断优先级
Extra > ADC Ring BufferADC 环形缓冲区

配置完成之后可以按下快捷键“Ctrl + S”保存,最后点击 “Generate Project Content” 按钮,让软件自动生成配置代码即可。

  1. 引脚配置信息

引脚配置还是在 pin_data.c 文件里生成。

const ioport_pin_cfg_t g_bsp_pin_cfg_data[] = {
{
.pin = BSP_IO_PORT_00_PIN_00,
.pin_cfg = ((uint32_t) IOPORT_CFG_ANALOG_ENABLE)
},
}

将 P000 复用为 IOPORT_CFG_ANALOG_ENABLE,也就是使能了 P000 的模拟量输入功能。

  1. ADC配置信息

ADC 设备对象会在 hal_data.c 中生成,名为 g_adc

const adc_instance_t g_adc0 =
{
.p_ctrl = &g_adc0_ctrl,
.p_cfg = &g_adc0_cfg,
.p_channel_cfg = &g_adc0_channel_cfg,
.p_api = &g_adc_on_adc
};

p_ctrl:它被用来保存 ADC 的信息,比如状态、寄存器、回调函数等。

p_cfg:指向 ADC 模块的配置信息结构体 g_adc0_cfg

const adc_cfg_t g_adc0_cfg =
{
.unit = 0,
.mode = ADC_MODE_SINGLE_SCAN,
.resolution = ADC_RESOLUTION_12_BIT,
.alignment = (adc_alignment_t) ADC_ALIGNMENT_RIGHT,
.trigger = (adc_trigger_t)0xF, // Not used
.p_callback = adc_callback,
}

p_channel_cfg:指向某个通道的配置值,也是来自 RASC 的配置。

const adc_channel_cfg_t g_adc0_channel_cfg =
{
.scan_mask = ADC_MASK_CHANNEL_0 | 0,
.scan_mask_group_b = 0,
.priority_group_a = ADC_GROUP_A_PRIORITY_OFF,
.add_mask = 0,
.sample_hold_mask = 0,
.sample_hold_states = 24,
#if ((0) | (0))
.p_window_cfg = (adc_window_cfg_t *) &g_adc0_window_cfg,
#else
.p_window_cfg = NULL,
#endif
};

p_api:ADC 模块的接口,指向在 r_adc.c 中实现的 g_adc_on_adc 结构体。用户可以使用 g_adc.p_api 中的函数指针来操作 ADC。

程序设计

实现 ADC 读取通道的电压值,ADC 通道 0 连接 P000 引脚,在 P000 用杜邦线接 3.3V,开启转换,读取 ADC 值,换算成电压值,通过串口 printf 发送格式化数据字符串,在串口助手观察数据。

  1. 打开 ADC 设备
R_ADC_Open(adc_ctrl_t * const p_ctrl, adc_cfg_t const * const p_cfg);

open 函数初始化 ADC,比如根据 p_cfg 来配置 ADC 的寄存器,设置中断回调函数等。但是这个函数没有完成对采样通道的初始化,采样通道的初始化需要调用 scanCfg 函数。

  1. 配置 ADC 扫描参数
R_ADC_ScanCfg(adc_ctrl_t * const p_ctrl, void const * const p_extend);

在调用 open 函数初始化了 ADC 后,还需要调用 scanCfg 配置指定的采样通道。第二个参数传入 g_adcp_channel_cfg 成员。

  1. 开启扫描
R_ADC_ScanStart(adc_ctrl_t * const p_ctrl);
  1. 读取 ADC 数据
R_ADC_Read(adc_ctrl_t * const p_ctrl, adc_channel_t const reg_id, uint16_t * const p_data);

用户通过 read 函数直接读取到 ADC 采样到的 12bit 的有效值。也可以调用 read32 函数读出采样值并转化为 32bit 的数据。

  1. 读取 ADC 转换状态
R_ADC_StatusGet(adc_ctrl_t * const p_ctrl, adc_status_t * p_status);

如果未启用中断,则可使用 R_ADC_StatusGet() 用于轮询 ADC 以确定扫描何时完成。读取 API 函数用于访问转换后的 ADC 结果。这适用于支持校准的 MCU 的普通扫描和校准扫描。

  1. ADC 转换完成中断回调
void adc_callback(adc_callback_args_t * p_args)

通过 ADC 的中断回调函数来判断 ADC 是否转换完成。我们需要定义一个布尔类型的数据 scan_complete_flag 来当做 ADC 读取完成的标志位。当没有转换完成的时候 scan_complete_flag 的值一直为 false,但 ADC 触发中断的时候将 scan_complete_flag 的值变为 true。

  1. ADC 值转换电压
v = (double)(adc_data*3.3/4095);

ADC 原始数据转换为电压值(ADC 参考电压为 3.3V),ADC 满量程值是 4095,对应 3.3V 电压。

程序编写

adc.h 头文件里定义中断初始化函数,数据读取函数。

#ifndef __BSP_ADC_H
#define __BSP_ADC_H

#include "hal_data.h"

void adc_Init(void);
double read_ADC_Value(void);

#endif

adc.c 里实现初始化函数、中断回调函数,数据读取函数。

#include "adc.h"

//ADC转换完成标志位
volatile bool scan_complete_flag = false;

//ADC转换完成中断
void adc_callback(adc_callback_args_t * p_args)
{
FSP_PARAMETER_NOT_USED(p_args);
scan_complete_flag = true;
}

//ADC初始化
void adc_Init(void)
{
fsp_err_t err;
err = R_ADC_Open(&g_adc0_ctrl, &g_adc0_cfg);
err = R_ADC_ScanCfg(&g_adc0_ctrl, &g_adc0_channel_cfg);
assert(FSP_SUCCESS == err);
}

/* 进行ADC采集,读取ADC数据并转换结果 */
double read_ADC_Value(void)
{
uint16_t adc_data;
double v;

(void)R_ADC_ScanStart(&g_adc0_ctrl);
while (!scan_complete_flag) //等待转换完成标志
{
;
}
scan_complete_flag = false; //重新清除标志位

/* 读取通道0数据 */
R_ADC_Read(&g_adc0_ctrl, ADC_CHANNEL_0, &adc_data);
/* ADC原始数据转换为电压值(ADC参考电压为3.3V) */
v = (double)(adc_data*3.3/4095);

return v;
}

hal_entry.c 文件中添加头文件 adc.h,在 hal_entry 入口函数初始化 adc,并添加调试语句,循环中 1s 执行一次读数据操作,并打印数据到串口。

void hal_entry(void)
{
/* TODO: add your own code here */

led_Init(); // LED 初始化
uart0_Init();
adc_Init();

printf("start ADC scan\r\n");

while(1)
{
LED1_TOGGLE();
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒

float adcV = (float)read_ADC_Value();

printf("ADC to v: %.2f\r\n", adcV);
}


#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}

下载验证

首先通过 USB 串口连接电脑和开发板,串口的 RX/TX 分别连接开发板的 TX/RX,交叉连接,电脑端打开串口调试助手,波特率设置 115200。

连接下载器,将程序编译并下载到开发板之后,按下复位按键来复位开发板,程序启动初始化完会打印数据。

把 P000 引脚用杜邦线连接开发板的 3.3V 引脚,数据打印显示是 3.3V。

插图05

把 P000 引脚用杜邦线连接开发板的 GND 引脚,数据打印显示是 0V。

插图06